<?php
/**
 * 使用方法：自动生成权限数据【根据文档注释】
 * @desc  \doc::$permission->reflection(接口所在绝对路径, 接口命令空间【不包含接口名称】);
 * @如下：
 * \doc::$permission->reflection(Yii::getAlias('@app_supplier').'/interfaces/', 'app_supplier\interfaces\\');
 */
namespace doc\analysis;

class permissions
{
    /**
     * @var \doc\analysis\permissions
     * @name 根目录
     */
    private  $baseDir = null;

    /**
     * @var \doc\analysis\permissions
     * @name #文件目录
     */
    public $dir = null;

    /**
     * @var \doc\analysis\permissions
     * @name #文件上级目录
     */

    public $baseName = null;
    /**
     * @var \doc\analysis\permissions
     * @name #命令空间
     */
    private  $namSpcae = null;

    /**
     * @var \doc\analysis\permissions
     * @name #数据库连接实例
     */
    private static  $db = null;

    /**
     * @param $dir
     * @param $namespace
     * @param null $search
     */
    public function reflection($dir, $namespace, $storage = false)
    {
        $this->dir = rtrim($dir, '/');
        $this->namSpcae = rtrim($namespace, '\\');

        //判断文件存在不
        if (is_dir($this->dir)) {
            if ($path = pathinfo($this->dir)) {
                $this->baseDir = $path['dirname'];
                $this->baseName = $path['basename'];
            }
            return $this->getFile();
        }
        return $this;
    }

    /**
     * 实例化数据库连接类
     * @param $db
     * @return $this
     */
    public function  setDb($db)
    {
        self::$db = $db;

        return $this;
    }


    /**
     * 循环读取目录下文件
     */
    private function getFile()
    {
        $dirs = [];
        //读取目录下的文件
        $files = scandir($this->dir);

        foreach ($files as $file) {
            if ($file !=='.' && $file !== '..') {
                $fileName = strpos($file, '.php') !== false ? rtrim($file, '.php') : $file;
                $dirs[$fileName] = $this->getInterfaceInfo($file);
            }
        }

        return $dirs;
    }

    /**
     * 获取【类】【接口】的具体信息
     * @param $fileName
     */
    private  function getInterfaceInfo($fileName)
    {
        $interfaces = [];
        //文件全路径[物理路径]
        $filePath = strpos($fileName, '.php') === false ? realpath($this->dir.'/'.$fileName.'.php') : realpath ($this->dir.'/'.$fileName);
        if (!file_exists($filePath)) {
            return [];
        }

        //命名空间+类名【接口】
        $namespace = strpos($fileName, '.php') === false ? $this->namSpcae.'\\'.$fileName : $this->namSpcae.'\\'.rtrim($fileName, '.php');

        include_once ($filePath);
        //开始获取决具体信息【反射机制】
        $reflection = new \ReflectionClass($namespace);
        //获取注释

        $classDoc = $reflection->getDocComment();
        $interfaces['md5'] = md5(sha1($classDoc));
        $alias = $reflection->isInterface() ? strtolower(substr(rtrim($fileName, '.php'),0,  -strlen('interface'))) : rtrim($fileName, '.php');

        $namespaceNum = (int)substr_count($this->namSpcae, '\\');
        switch ($namespaceNum) {
            //一般模式
            case 1:

                list($space, $dir) = explode('\\', $this->namSpcae);
                $interfaces['alias'] = $alias;
                $interfaces['subclass_namespace'] = $space.'\\controllers\\'.ucfirst($alias).'Controller';
                $interfaces['interface_name'] = rtrim($fileName, '.php');
                break;
            case 2:
                //一般模式
                list($space, $dir) = explode('\\', $this->namSpcae);
                $interfaces['alias'] = $alias;
                $interfaces['subclass_namespace'] = $space.'\\controllers\\'.ucfirst($alias).'Controller';
                $interfaces['interface_name'] = rtrim($fileName, '.php');
                break;
            case 3:
                //模块方式
                list($dir, $modules, $m) = explode('\\', $this->namSpcae);
                $interfaces['alias'] = $m.'_'.$alias;
                $interfaces['subclass_namespace'] = $dir.'\\'.$modules.'\\'.$m.'\\controllers\\'.ucfirst($alias).'Controller';
                $interfaces['interface_name'] = rtrim($fileName, '.php');
                break;

        }


        $interfaces['interface_namespace'] = $namespace;
        $interfaces['start_line'] = $reflection->getStartLine();
        $interfaces['end_line'] = $reflection->getEndLine();
        $interfaces['title'] = $this->getStrRegular('name', $classDoc);
        $interfaces['sort'] = $this->getStrRegular('sort', $classDoc, 0);
        $interfaces['author'] = $this->getStrRegular('author', $classDoc);
        $interfaces['remark'] = $this->getStrRegular('remark', $classDoc);
        $interfaces['deprecated'] = $this->getStrRegular('deprecated', $classDoc, 0);//停用
        $interfaces['methods'] = $this->parseMethod($reflection, isset($interfaces['alias']) ? $interfaces['alias']: null);

        return $interfaces;

    }

    /**
     * 获取【类】【接口】方法的具体信息
     * @param \ReflectionClass $reflection
     */
    private function parseMethod(\ReflectionClass $reflection, $router=null)
    {
        $list = [];
        if ($router && strpos($router, '_') !== false) {
            $router = str_replace('_', '/', $router);
        }
        $methods = $reflection->getMethods();

        if (!empty($methods) && is_array($methods)) {
            foreach ($methods as $v) {
                $doc = $v->getDocComment();
                $action = strtolower(substr($v->getName(), strlen('action')));
                $route = !empty($router) ? rtrim($router, '/') . '/'. $action : $action;
                $alias = str_replace('/', '_', $route);
                $arr = [
                    'title'=>$this->getStrRegular('name', $doc),
                    'remark'=>$this->getStrRegular('remark', $doc, 'page'),
                    'name'=>$action,
                    'action_name'=>$v->getName(),
                    'start_line'=>$v->getStartLine(),
                    'end_line'=>$v->getEndLine(),
                    'route'=>$route,
                    'alias'=>$alias,
                    'md5'=>md5($v->getName().$doc),
                    'type'=>$this->getStrRegular('type', $doc, 'page'),
                    'sort'=>$this->getStrRegular('sort', $doc, 1),
                    'guest'=>$this->getStrRegGuest('guest', $doc, 0),//游客
                ];
                //非游客的通用权限；no：；post：post所有权限；get：get所有权限；all：所有权限[不验证权限]
                $common = $this->getStrRegBool('common', $doc);
                $commonValue = $this->getStrRegular('common', $doc);
                $arr['common'] = $common ? (!empty($commonValue) ? $commonValue : 'all') : 'no';
                $arr['postall'] = $this->getStrRegBool('postall', $doc);
                $arr['getall'] = $this->getStrRegBool('getall', $doc);

                $arr['get'] = $this->getStrMethodArgc('get', $doc);
                $arr['post'] = $this->getStrMethodArgc('post', $doc);

                if ($arr['common']==='get' && count($arr['post']) > 1) {
                    $arr['postall'] = true;
                }

                if ($arr['common']==='post' && count($arr['get']) > 1) {
                    $arr['getall'] = true;
                }

                if ($arr['common']==='no') {
                    if (count($arr['post']) > 1) {
                        $arr['postall'] = true;
                    }

                    if (count($arr['get']) > 1) {
                        $arr['getall'] = true;
                    }
                }

                //权限类型，0：界面权限；1：操作权限；2：界面+操作权限；3：菜单权限
                $list[]=$arr;
            }
        }

        return $list;
    }


    public function cur()
    {
        return $this->getInterfaceInfo('');
    }
    /**
     * @param $data
     */
    private function storage($data)
    {

    }

    /**
     * 解析注释日志
     * @param $key
     * @param $str
     * @param null $default
     * @return null|string
     */
    private function getStrRegular($key, $str, $default=null)
    {

        $regular = "/(?<{$key}>@{$key}.+)/";
        preg_match($regular, $str, $arr);

        return isset($arr[$key]) ? trim(ltrim($arr[$key], "@{$key} ")) : $default;
    }

    /**
     * 是不是游客可以访问
     * @param $key
     * @param $str
     * @param null $default
     * @return int|null
     */
    private function getStrRegGuest($key, $str, $default=null)
    {
        $regular = "/(?<{$key}>@{$key}.+)/";
        preg_match($regular, $str, $arr);

        return isset($arr[$key]) ? 1: $default;

    }

    /**
     * 获取get与post所有权限
     * @param $key
     * @param $str
     * @param string $default
     * @return array
     */
    private function getStrMethodArgc($key, $str, $default='')
    {
        $option = [];
        $regular = "/(?<{$key}>\@{$key} .+)/";
        preg_match_all($regular, $str, $argc);

        if (isset($argc[$key]) && is_array($argc[$key])) {
            foreach ($argc[$key] as $k=>$v) {
                $strStart =  trim(ltrim($v, "\@{$key}"));
                $strLine = explode(',',$strStart);
                $arr=[];
                foreach ($strLine as $item) {
                    list($i, $value) = explode(":", $item);
                    switch ($i) {
                        case 'k':
                            $arr['key']=$value;
                            break;
                        case 'v':
                            $arr['value']=$value;
                            break;
                        case 't':
                            $arr['title']=$value;
                            break;
                    }
                }
                $option[]= $arr;
            }
        }

        return $option;
    }

    /**
     * 获取当前action权限设置：
     * @param $key
     * @param $str
     * @param null $default
     */
    private function getStrRegBool($key, $str)
    {
        $regular = "/(?<{$key}>@{$key}.+)/";
        preg_match($regular, $str, $arr);

        return isset($arr[$key]) ? true :false;
    }


    /**
     * 不去除空格
     * @param $key
     * @param $str
     * @param null $default
     * @return null|string
     */
    private  function getStrRegSpace($key, $str, $default=null)
    {
        $regular = "/(?<{$key}>@{$key}.+)/";
        preg_match($regular, $str, $arr);

        return isset($arr[$key]) ? trim(ltrim($arr[$key], "@{$key}")) : $default;
    }

}